package dsx.rpcProxy;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;

import dsx.inf.IAnswChecker;
import dsx.inf.IConverter;
import dsx.inf.ISlicer;

public class UdpClient {
	Object lock = new Object();
	DatagramSocket sock = null;
	DatagramPacket recvPacket = new DatagramPacket(new byte[1500], 1500);

	ArrayList<_UdpConfig> configLst = null;
	int connIndex = 0;
	ISlicer slicer;
	IConverter converter;

	public void start(ArrayList<_UdpConfig> configLst, ISlicer slicer,
			IConverter converter) throws Exception {
		synchronized (lock) {
			this.configLst = configLst;
			connIndex = 0;
			this.slicer = slicer;
			this.converter = converter;
			this.sock = new DatagramSocket();
		}
	}

	public void stop() {
		synchronized (lock) {
			try {
				if (sock != null)
					sock.close();
			} catch (Exception ex) {

			}
		}
	}

	private Object doRequest(_UdpConfig config, Object reqObj, Class<?> answType)
			throws Exception {
		this.sock.setSoTimeout(config.timeout);
		// 发送
		byte[] sendData = converter.toBytes(reqObj);
		if (sendData == null)
			return null;
		byte[] sendBuff;
		if (slicer == null)
			sendBuff = sendData;
		else
			sendBuff = slicer.pack(sendData);
		InetAddress inetAddr = InetAddress.getByName(config.ip);
		DatagramPacket sendPacket = new DatagramPacket(sendBuff,
				sendBuff.length, inetAddr, config.port);
		sock.send(sendPacket);
		// 接收
		while (true) {
			sock.receive(recvPacket);
			if (recvPacket.getAddress().toString().equals(inetAddr.toString())
					&& recvPacket.getPort() == config.port) {
				if (recvPacket.getLength() > 0) {
					byte[] answ = Arrays.copyOfRange(recvPacket.getData(), 0,
							recvPacket.getLength());
					Object answObj = converter.toObject(answ, answType);
					if (reqObj instanceof IAnswChecker) {
						if (!((IAnswChecker) reqObj).checkAnsw(answObj)) {
							continue;
						}
					}
					return answObj;
				} else {
					return null;
				}
			}
		}
	}

	public Object request(Object reqObj, Class<?> answType) {
		synchronized (lock) {
			for (int i = 0; i < configLst.size(); i++) {
				try {
					return doRequest(configLst.get(connIndex), reqObj, answType);
				} catch (Exception ex) {
				}
				connIndex++;
				if (connIndex >= configLst.size())
					connIndex = 0;
			}
			return null;
		}
	}

	public Object request(Object reqObj, Class<?> answType, String host,
			int port) {
		synchronized (lock) {
			_UdpConfig config = new _UdpConfig();
			config.setIp(host);
			config.setPort(port);
			try {
				return doRequest(config, reqObj, answType);
			} catch (Exception ex) {
			}
			return null;
		}
	}

}
